#include "WindowGroups.h"

///Copyright (c) 2009-2010 T. Klepzig 

WindowGroups::WindowGroups()
{
    buttonOpen = 0;
    animation = 0;
    currentGroupID = 0;
    tmpGroupIDForRenaming = -1;
    tag = 0;

    onGroupNameChangedPtr = 0;
    onGroupSwitchingPtr = 0;

    opened = false;
    hotkeyOpened = false;
    
    lastGroupID = 9;
    normalHeight = 38;
    openedHeight = 390;	

    location.x = CW_USEDEFAULT;
    location.y = CW_USEDEFAULT;

    LoadSettings();
}

WindowGroups::~WindowGroups()
{
    SaveSettings();

    Deactivate();

    delete buttonOpen;
    buttonOpen = 0;

    if (!groupButtons.empty())
    {
        map<int, PaintedButton*>::iterator iter;
        for (iter = groupButtons.begin(); iter != groupButtons.end(); iter++)
        {
            delete iter->second;
        }
        groupButtons.clear();
    }
    
    DestroyMenu(groupMenu);

    delete animation;
    animation = 0;
    
    delete windowVisibilityHelper;
    windowVisibilityHelper = 0;
}

void WindowGroups::Create(HINSTANCE hInstance)
{
    BOOL dwmEnabled;
    DwmIsCompositionEnabled(&dwmEnabled);

    windowVisibilityHelper = new WindowVisibilityHelper(hInstance);
    windowVisibilityHelper->SetTag((LPVOID)this);

    CustomWindow::Create(hInstance,
                         TEXT("WindowGroupsWindow"),
                         WndProc,
                         WS_EX_TOOLWINDOW,
                         !dwmEnabled ? WS_POPUP : WS_THICKFRAME,
                         !dwmEnabled ? CS_VREDRAW | CS_NOCLOSE | CS_DBLCLKS : CS_NOCLOSE | CS_DBLCLKS,
                         !dwmEnabled ? GetSysColorBrush(COLOR_GRADIENTACTIVECAPTION) : GetStockBrush(BLACK_BRUSH),
                         0,
                         LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1)));

    this->SetText(Translate(TEXT("WindowGroups")));
    this->SetLocation(location);
    this->SetSize(180, opened ? openedHeight : normalHeight, true);
    
    animation = new SmoothWindowSideAnimation(this->GetHWnd(), 0);

    groupMenu = CreatePopupMenu();
    AppendMenu(groupMenu, MF_STRING, 1, Translate(TEXT("Rename")).c_str());

    buttonOpen = new PaintedButton(this->GetHWnd());
    buttonOpen->Clicked(buttonOpen_Clicked);
    buttonOpen->SetSize(28, 28);
    buttonOpen->SetLocation(this->GetSize().cx - 5 - buttonOpen->GetSize().cx, 5);
    buttonOpen->Show();

    map<int, tstring>::iterator iter;
    for (iter = groups.begin(); iter != groups.end(); iter++)
    {
        PaintedButton* tmpButton = new PaintedButton(this->GetHWnd());
        tmpButton->RightMouseButtonUp(groupButton_RightMouseButtonUp);
        tmpButton->Clicked(groupButton_Clicked);
        tmpButton->SetCheckable(true);

        if (!iter->first)
        {
            tmpButton->SetChecked(true);
            tmpButton->SetText(iter->second);
        }
        else
        {
            tmpButton->SetText(iter->second);
        }

        tmpButton->SetTag((LPVOID)iter->first);
        tmpButton->SetLocation(8, 45 + iter->first * 34);
        tmpButton->SetSize(this->GetSize().cx - 16, 28);
        if (!dwmEnabled)
            tmpButton->SetTextColor(RGB(0, 0, 0));

        tmpButton->Show();
        groupButtons[iter->first] = tmpButton;
    }

    BOOL bl=TRUE;
    DwmSetWindowAttribute(this->GetHWnd(),DWMWA_DISALLOW_PEEK,&bl, sizeof(bl));
    DwmSetWindowAttribute(this->GetHWnd(),DWMWA_EXCLUDED_FROM_PEEK,&bl, sizeof(bl));

    DWMFLIP3DWINDOWPOLICY flip3DPolicy = DWMFLIP3D_EXCLUDEBELOW;
    DwmSetWindowAttribute(this->GetHWnd(), DWMWA_FLIP3D_POLICY, &flip3DPolicy, sizeof(DWMWA_FLIP3D_POLICY));	
}


CCState WindowGroups::Activate()
{
    CCState result;
    if ((result = CassiopeiaComponent::Activate()) != CCState::Successful)
        return result;

    Utilities::MoveWindowIntoVisibleArea(this->GetHWnd());
   
    this->ShowNoActivate();
    SetWindowPos(this->GetHWnd(), Utilities::GetLastValidTopLevelWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

    RegisterHotKey(this->GetHWnd(), 1, MOD_WIN, 71);
    groupButtons[0]->PerformClick();

    return CCState::Successful;
}

CCState WindowGroups::Deactivate()
{
    CCState result;
    if ((result = CassiopeiaComponent::Deactivate()) != CCState::Successful)
        return result;

    this->Hide();

    SwitchToGroup(0);
    UnregisterHotKey(this->GetHWnd(), 1);	

    return CCState::Successful;
}


void WindowGroups::GroupNameChanged(GroupNameChangedProc ptr)
{
    onGroupNameChangedPtr = ptr;
}

void WindowGroups::GroupSwitching(GroupSwitchingProc ptr)
{
    onGroupSwitchingPtr = ptr;
}


void WindowGroups::MoveWindowToGroup(int groupID, HWND hWnd)
{	
    if (excludedHWnds.find(hWnd) != excludedHWnds.end())
        return;

    //remove from all groups except the new one
    map<int, set<HWND>>::iterator iter;
    for (iter = hiddenHWnds.begin(); iter != hiddenHWnds.end(); iter++)
    {
        if (iter->first && iter->first != groupID)
        {
            iter->second.erase(hWnd);

            if (currentGroupID == iter->first)
                windowVisibilityHelper->Hide(hWnd);
        }
    }

    if (!groupID)
        return;

    //add to group
    hiddenHWnds[groupID].insert(hWnd);

    if (currentGroupID == groupID)
        windowVisibilityHelper->Show(hWnd);
}

void WindowGroups::ExcludeWindow(HWND hWnd, bool exclude)
{
    if (exclude)
        excludedHWnds.insert(hWnd);
    else
        excludedHWnds.erase(hWnd);
}

bool WindowGroups::IsWindowExcluded(HWND hWnd)
{
    return excludedHWnds.find(hWnd) != excludedHWnds.end();
}

map<int, tstring> WindowGroups::GetGroups()
{
    return groups;
}

int WindowGroups::GetCurrentGroup()
{
    return currentGroupID;
}

void WindowGroups::SetGroupName(int groupID, tstring name)
{
    groups[groupID] = name;
    groupButtons[groupID]->SetText(name);

    if (groupID && onGroupNameChangedPtr)
        onGroupNameChangedPtr(this, groupID, name);
}

bool WindowGroups::SwitchToGroup(int groupID)
{
    if (currentGroupID == groupID)
        return false;

    bool cancel = false;

    if (onGroupSwitchingPtr)
        onGroupSwitchingPtr(this, groupID, &cancel);

    if (cancel)
        return false;

    //Hide
    if (hiddenHWnds.find(currentGroupID) != hiddenHWnds.end())
        hiddenHWnds[currentGroupID].clear();

    EnumWindows(EnumWindowsProc, (LPARAM)this);


    //Show
    if (hiddenHWnds.find(groupID) != hiddenHWnds.end())
    {
        set<HWND>::iterator iter;
        for (iter = hiddenHWnds[groupID].begin(); iter != hiddenHWnds[groupID].end(); iter++)
        {
            windowVisibilityHelper->Show(*iter);
        }
    }

    currentGroupID = groupID;

    return true;
}

void WindowGroups::Reset()
{
    windowVisibilityHelper->Recover();
}

void WindowGroups::SetTag(LPVOID tag)
{
    this->tag = tag;
}

LPVOID WindowGroups::GetTag()
{
    return tag;
}


void WindowGroups::Open(bool value)
{
    if (!animation->IsRunning())
    {
        if (value && !opened)	
        {
            animation->StartAnimation(53, normalHeight, openedHeight - normalHeight, WindowEdge::WE_HEIGHT);
            opened = true;
        }
        else if (!value && opened)
        {
            animation->StartAnimation(41, openedHeight, normalHeight - openedHeight, WindowEdge::WE_HEIGHT);
            opened = false;
        }

        buttonOpen->Refresh();
    }
}

void WindowGroups::ToggleOpen()
{
    Open(!opened);
}

void WindowGroups::ChangeDwmCompatibleLayout()
{
    BOOL dwmEnabled;
    DwmIsCompositionEnabled(&dwmEnabled);

    if (dwmEnabled)
    {
        SetClassLongPtr(this->GetHWnd(),GCL_HBRBACKGROUND,(LONG)GetStockObject(BLACK_BRUSH));
        SetClassLongPtr(this->GetHWnd(),GCL_STYLE, GetClassLongPtr(this->GetHWnd(), GCL_STYLE) & ~CS_VREDRAW);

        SetWindowLongPtr(this->GetHWnd(), GWL_STYLE, GetWindowLongPtr(this->GetHWnd(), GWL_STYLE) & ~WS_POPUP);
        SetWindowLongPtr(this->GetHWnd(), GWL_STYLE, GetWindowLongPtr(this->GetHWnd(), GWL_STYLE) | WS_THICKFRAME);
        SetWindowLongPtr(this->GetHWnd(), GWL_STYLE, GetWindowLongPtr(this->GetHWnd(), GWL_STYLE) | WS_CAPTION);


        if (!groupButtons.empty())
        {
            map<int, PaintedButton*>::iterator iter;
            for (iter = groupButtons.begin(); iter != groupButtons.end(); iter++)
            {
                if (iter->second)
                    iter->second->SetTextColor(RGB(255, 255, 255));
            }
        }
    }
    else
    {
        SetClassLongPtr(this->GetHWnd(),GCL_HBRBACKGROUND,(LONG)GetSysColorBrush(COLOR_GRADIENTACTIVECAPTION));
        SetClassLongPtr(this->GetHWnd(),GCL_STYLE, GetClassLongPtr(this->GetHWnd(), GCL_STYLE) | CS_VREDRAW);

        SetWindowLongPtr(this->GetHWnd(), GWL_STYLE, GetWindowLongPtr(this->GetHWnd(), GWL_STYLE) | WS_POPUP);
        SetWindowLongPtr(this->GetHWnd(), GWL_STYLE, GetWindowLongPtr(this->GetHWnd(), GWL_STYLE) & ~WS_THICKFRAME);
        SetWindowLongPtr(this->GetHWnd(), GWL_STYLE, GetWindowLongPtr(this->GetHWnd(), GWL_STYLE) & ~WS_CAPTION);

        if (!groupButtons.empty())
        {
            map<int, PaintedButton*>::iterator iter;
            for (iter = groupButtons.begin(); iter != groupButtons.end(); iter++)
            {
                if (iter->second)
                    iter->second->SetTextColor(RGB(0, 0, 0));
            }
        }
    }

    InvalidateRgn(this->GetHWnd(), 0, TRUE);
}

bool WindowGroups::HandleGroupButtonWndProcs(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (!groupButtons.empty())
    {
        map<int, PaintedButton*>::iterator iter;
        for (iter = groupButtons.begin(); iter != groupButtons.end(); iter++)
        {
            if (iter->second && iter->second->HandleWndProc(hWnd, uMsg, wParam, lParam))
                return true;
        }
    }

    return false;
}

void WindowGroups::HandleGroupButtonsPainting(HDC hdc)
{
    if (!groupButtons.empty())
    {
        map<int, PaintedButton*>::iterator iter;
        for (iter = groupButtons.begin(); iter != groupButtons.end(); iter++)
        {
            if (iter->second)
                iter->second->HandlePainting(hdc);
        }
    }
}

void WindowGroups::LoadSettings()
{
    Settings settings;
    Settings::Data data = {0};
    settings.Load(&data);

    location = data.WindowGroupsData.Location;
    opened = data.WindowGroupsData.Opened;

    if (data.WindowGroupsData.Groups.empty())
    {
        for (int i = 0; i < (lastGroupID + 1); i++)
        {
            if (!i)
            {
                groups[i] = Translate(TEXT("All Windows"));
            }
            else
            {
                TCHAR number[10] = {0};
                _stprintf(number, TEXT("# %d"), i);
                groups[i] = Translate(2, TEXT("Group"), number);
            }
        }
    }
    else
    {
        groups = data.WindowGroupsData.Groups;
    }
}

void WindowGroups::SaveSettings()
{
    Settings settings;
    Settings::Data data = {0};
    settings.Load(&data);

    data.WindowGroupsData.Location = this->GetLocation();
    data.WindowGroupsData.Opened = opened;
    data.WindowGroupsData.Groups = groups;

    settings.Save(data);
}



void WindowGroups::buttonOpen_Clicked(PaintedButton* sender, LPVOID tag)
{
    WindowGroups* instance = (WindowGroups*)instances[sender->GetParentWindow()];
    instance->hotkeyOpened = false;
    instance->ToggleOpen();
}

void WindowGroups::groupButton_Clicked(PaintedButton* sender, LPVOID tag)
{
    WindowGroups* instance = (WindowGroups*)instances[sender->GetParentWindow()];

    map<int, PaintedButton*>::iterator iter;
    for (iter = instance->groupButtons.begin(); iter != instance->groupButtons.end(); iter++)
    {
        if (iter->second != sender)
            iter->second->SetChecked(false);
    }

    if (!instance->SwitchToGroup((int)tag))
        sender->SetChecked(true);
}

void WindowGroups::groupButton_RightMouseButtonUp(PaintedButton* sender, LPVOID tag)
{
    WindowGroups* instance = (WindowGroups*)instances[sender->GetParentWindow()];
    
    POINT cursorPos = Utilities::GetCursorPosition();
    UINT_PTR menuId = TrackPopupMenu(instance->groupMenu, TPM_RETURNCMD | TPM_NONOTIFY | TPM_VERNEGANIMATION | TPM_LEFTBUTTON, cursorPos.x, cursorPos.y, 0, instance->GetHWnd(), 0);

    int groupID = (int)sender->GetTag();

    switch (menuId)
    {
    case 1:
        {
            EnableMenuItem(instance->groupMenu, 1, MF_DISABLED);
            instance->tmpGroupIDForRenaming = groupID;
            DialogBoxParam(instance->hInstance, MAKEINTRESOURCE(IDD_DIALOG4), 0, DialogProc, (LPARAM)instance);	
            EnableMenuItem(instance->groupMenu, 1, MF_ENABLED);
            break;
        }
    }
}


BOOL CALLBACK WindowGroups::EnumWindowsProc(HWND hWnd, LPARAM lParam)
{	
    WindowGroups* instance = (WindowGroups*)lParam;

    TCHAR windowText[255] = {0};
    GetWindowText(hWnd, windowText, 255);

    if (IsWindow(hWnd)
        && IsWindowVisible(hWnd)
        && !Utilities::IsSpecialWindow(hWnd)
        && !Utilities::IsSpecialCassiopeiaWindow(hWnd)
        && instance->excludedHWnds.find(hWnd) == instance->excludedHWnds.end()
        && !instance->windowVisibilityHelper->IsHidden(hWnd)
        && _tcscmp(windowText, TEXT("WinTools (Running) - Microsoft Visual Studio (Administrator)"))
        && _tcscmp(windowText, TEXT("WinTools (Running) - Microsoft Visual Studio")))
    {
        //add to default group
        if (instance->currentGroupID && instance->hiddenHWnds.find(0) != instance->hiddenHWnds.end())
        {
            if (instance->hiddenHWnds[0].find(hWnd) == instance->hiddenHWnds[0].end())
                instance->hiddenHWnds[0].insert(hWnd);
        }

        instance->hiddenHWnds[instance->currentGroupID].insert(hWnd);
        instance->windowVisibilityHelper->Hide(hWnd);
    }

    return TRUE;
}

LRESULT CALLBACK WindowGroups::WndProc(LPVOID sender, HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    static BOOL	dwmEnabled = -1;
    static bool hotkeyPressed = false;

    WindowGroups* instance = (WindowGroups*)instances[hWnd];

    if (dwmEnabled == -1)
        DwmIsCompositionEnabled(&dwmEnabled);


    if (instance->buttonOpen->HandleWndProc(hWnd, uMsg, wParam, lParam))
        return DefWindowProc(hWnd, uMsg, wParam, lParam);

    if (instance->HandleGroupButtonWndProcs(hWnd, uMsg, wParam, lParam))
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    
    switch (uMsg)
    {
    case WM_KEYUP:
        {
            if (!instance->IsActive())
                break;

            if (wParam == 'G' && hotkeyPressed)
            {
                hotkeyPressed = false;
                break;
            }

            if ((wParam == VK_NUMPAD0 || wParam == '0') && instance->groupButtons.find(0) != instance->groupButtons.end())
                instance->groupButtons[0]->PerformClick();
            else if ((wParam == VK_NUMPAD1 || wParam == '1') && instance->groupButtons.find(1) != instance->groupButtons.end())
                instance->groupButtons[1]->PerformClick();
            else if ((wParam == VK_NUMPAD2 || wParam == '2') && instance->groupButtons.find(2) != instance->groupButtons.end())
                instance->groupButtons[2]->PerformClick();
            else if ((wParam == VK_NUMPAD3 || wParam == '3') && instance->groupButtons.find(3) != instance->groupButtons.end())
                instance->groupButtons[3]->PerformClick();
            else if ((wParam == VK_NUMPAD4 || wParam == '4') && instance->groupButtons.find(4) != instance->groupButtons.end())
                instance->groupButtons[4]->PerformClick();
            else if ((wParam == VK_NUMPAD5 || wParam == '5') && instance->groupButtons.find(5) != instance->groupButtons.end())
                instance->groupButtons[5]->PerformClick();
            else if ((wParam == VK_NUMPAD6 || wParam == '6') && instance->groupButtons.find(6) != instance->groupButtons.end())
                instance->groupButtons[6]->PerformClick();
            else if ((wParam == VK_NUMPAD7 || wParam == '7') && instance->groupButtons.find(7) != instance->groupButtons.end())
                instance->groupButtons[7]->PerformClick();
            else if ((wParam == VK_NUMPAD8 || wParam == '8') && instance->groupButtons.find(8) != instance->groupButtons.end())
                instance->groupButtons[8]->PerformClick();
            else if ((wParam == VK_NUMPAD9 || wParam == '9') && instance->groupButtons.find(9) != instance->groupButtons.end())
                instance->groupButtons[9]->PerformClick();
            else if (!instance->groupButtons.empty())
            {
                int cycleCounter = 0;
                map<int, PaintedButton*>::iterator iter;

                if (instance->currentGroupID != instance->lastGroupID && instance->groupButtons.find(instance->currentGroupID + 1) != instance->groupButtons.end() && instance->groupButtons[instance->currentGroupID + 1]->GetText().length() > 0 && tolower(instance->groupButtons[instance->currentGroupID]->GetText()[0]) == tolower(wParam))
                {
                    iter = instance->groupButtons.find(instance->currentGroupID);
                }
                else
                {
                    cycleCounter++;
                    iter = instance->groupButtons.begin();
                }

                do
                {
                    for (iter; iter != instance->groupButtons.end(); iter++)
                    {
                        if (iter->first != instance->currentGroupID && iter->second->GetText().length() > 0 && tolower(iter->second->GetText()[0]) == tolower(wParam))
                        {
                            cycleCounter++;
                            iter->second->PerformClick();
                            break;
                        }
                    }

                    cycleCounter++;
                    iter = instance->groupButtons.begin();

                } while(cycleCounter < 2);
            }
            break;
        }
    case WM_HOTKEY:
        {
            if (wParam == 1)
            {
                hotkeyPressed = true;
                Utilities::MoveWindowIntoVisibleArea(hWnd);
                SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
                SetForegroundWindow(hWnd);

                if (!instance->opened)
                {
                    instance->hotkeyOpened = true;
                    instance->Open(true);
                }
            }

            break;
        }
    case WM_ACTIVATE:
        {
            if (wParam == WA_INACTIVE)
            {
                SetWindowPos(hWnd, Utilities::GetLastValidTopLevelWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

                if (instance->hotkeyOpened)
                {
                    instance->Open(false);
                    instance->hotkeyOpened = false;
                }
            }
			else
			{
				instance->Refresh();
			}

            break;
        }
    case WM_LBUTTONDBLCLK:
        {
            instance->hotkeyOpened = false;
            instance->ToggleOpen();
            break;
        }
    case WM_LBUTTONDOWN:
        {
            SendMessage(hWnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0);

            //in "case WM_MOUSEMOVE:"
            /*if (wParam == MK_LBUTTON)
            SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);*/

            break;
        }
    case WM_DWMCOMPOSITIONCHANGED:
        {
            DwmIsCompositionEnabled(&dwmEnabled);
            instance->ChangeDwmCompatibleLayout();
            break;
        }
    case WM_MOVING:
        {
            if (instance->animation && instance->animation->IsRunning())
            {
                //disable moving during the animation, otherwise the animation would be
                //cancelled when moving the window during the animation progress

                RECT cb = instance->GetBounds();
                RECT* r = (RECT*)lParam;

                r->left = cb.left;
                r->top = cb.top;
                r->right = cb.right;
                r->bottom = cb.bottom;

                return TRUE;
            }

            break;
        }
    case WM_PAINT:
        {		
            HDC          hdc;
            PAINTSTRUCT  ps;

            hdc = BeginPaint(hWnd, &ps);

            RECT clientRect;
            GetClientRect(hWnd,&clientRect);	
            clientRect.bottom = instance->normalHeight;

            //draw text
			Utilities::DrawDwmCompatibleText(clientRect, hdc, instance->GetText().c_str(), 13, RGB(0, 0, 0), DT_LEFT | DT_VCENTER | DT_SINGLELINE, FW_MEDIUM, 12, 9, 0);
            
            //draw extra dwmColor; if it looks bad during resizing, remove it
            {
                /*Color dwmColor = 0;
                DWORD cr;
                BOOL opaqueBlend;
                DwmGetColorizationColor(&cr, &opaqueBlend);
                cr = 0x3C000000 | (cr & 0x00FFFFFF);
                dwmColor.SetValue(cr);

                GetClientRect(hWnd,&clientRect);	
                clientRect.bottom = 426;

                SolidBrush brush(dwmColor);
                GraphicsPath fillPath;

                Graphics graphics(hdc);
                graphics.SetSmoothingMode(SmoothingModeAntiAlias);

                Utilities::CreateRoundRectPath(&fillPath, clientRect, 12);
                graphics.FillPath(&brush,&fillPath);*/
            }

            //draw horizontal line
            Graphics graphics(hdc);
            graphics.SetSmoothingMode(SmoothingModeAntiAlias);
            Pen pen1(Color(80, 30, 30, 30));
            Pen pen2(Color(80, 240, 240, 240));
            graphics.DrawLine(&pen1, 15, instance->normalHeight + 1, instance->GetSize().cx - 15, instance->normalHeight + 1);
            graphics.DrawLine(&pen2, 15, instance->normalHeight + 2, instance->GetSize().cx - 15, instance->normalHeight + 2);

            //draw dwmDisabled Color
            if (!dwmEnabled)
            {
                Pen border1(Color(0, 0, 0));
                Pen border2(Color(230, 230, 230));
                graphics.DrawRectangle(&border1, 0, 0, instance->GetSize().cx - 1, instance->GetSize().cy - 1);
                graphics.DrawRectangle(&border2, 1, 1, instance->GetSize().cx - 3, instance->GetSize().cy - 3);
            }

            //draw arrow
            if (dwmEnabled)
                Utilities::DrawPNGFromResource(instance->hInstance, hdc, MAKEINTRESOURCE(instance->opened ? IDB_ArrowUp : IDB_ArrowDown), instance->GetSize().cx - 5 - instance->buttonOpen->GetSize().cx + ((instance->buttonOpen->GetSize().cx - 12) / 2), 5 + ((instance->buttonOpen->GetSize().cx - 12) / 2), 12, 12);
            else
                Utilities::DrawPNGFromResource(instance->hInstance, hdc, MAKEINTRESOURCE(instance->opened ? IDB_ArrowBlackUp : IDB_ArrowBlackDown), instance->GetSize().cx - 5 - instance->buttonOpen->GetSize().cx + ((instance->buttonOpen->GetSize().cx - 12) / 2), 5 + ((instance->buttonOpen->GetSize().cx - 12) / 2), 12, 12);

            
            //button painting
            instance->buttonOpen->HandlePainting(hdc);
            instance->HandleGroupButtonsPainting(hdc);
            
            EndPaint(hWnd, &ps);

			break;
        }
    case WM_DISPLAYCHANGE:
        {
            instance->Refresh();
            break;
        }
	case WM_SIZE:
		{
            Utilities::MoveWindowIntoVisibleArea(hWnd);
			break;
		}
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

INT_PTR CALLBACK WindowGroups::DialogProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static map<HWND, WindowGroups*> dlgInstances;

    switch(uMsg)
    {
    case WM_INITDIALOG:
        {
            dlgInstances[hWndDlg] = (WindowGroups*)lParam;

            HICON icon = LoadIcon(dlgInstances[hWndDlg]->hInstance, MAKEINTRESOURCE(IDI_ICON1));
            SendMessage(hWndDlg, WM_SETICON, ICON_SMALL, (LPARAM)icon);
            SendMessage(hWndDlg, WM_SETICON, ICON_BIG, (LPARAM)icon);

            SetWindowText(hWndDlg, Translate(TEXT("Rename Group")).c_str());
            SetDlgItemText(hWndDlg, IDC_STATIC_NAME, Translate(2, TEXT("Name"), TEXT("#:")).c_str());
            SetDlgItemText(hWndDlg, IDOK, Translate(TEXT("OK")).c_str());

            RECT rect = {0};
            GetWindowRect(hWndDlg,&rect);
            POINT pos = Utilities::CenterScreen(RECTWIDTH(rect), RECTHEIGHT(rect));
            SetWindowPos(hWndDlg, 0, pos.x, pos.y, 0, 0, SWP_NOSIZE);

            int groupID = dlgInstances[hWndDlg]->tmpGroupIDForRenaming;

            if (dlgInstances[hWndDlg]->groups.find(groupID) != dlgInstances[hWndDlg]->groups.end())
                Edit_SetText(GetDlgItem(hWndDlg, IDC_EDIT_GRPNAME), dlgInstances[hWndDlg]->groups[groupID].c_str());

            Edit_SetSel(GetDlgItem(hWndDlg, IDC_EDIT_GRPNAME), 0, -1);

            break;
        }
    case WM_DISPLAYCHANGE:
        {
            Utilities::MoveWindowIntoVisibleArea(hWndDlg);
            break;
        }
    case WM_COMMAND:
        {
            if (HIWORD(wParam) == BN_CLICKED)
            {
                switch (LOWORD(wParam))
                {
                case IDOK:
                    {
                        TCHAR buffer[255] = {0};
                        Edit_GetText(GetDlgItem(hWndDlg, IDC_EDIT_GRPNAME), buffer, 255);
                        
                        tstring tmp(buffer);
                        dlgInstances[hWndDlg]->SetGroupName(dlgInstances[hWndDlg]->tmpGroupIDForRenaming, tmp);
                        
                        //fall through
                    }
                case IDCANCEL:
                    {
                        EndDialog(hWndDlg, wParam);
                        dlgInstances[hWndDlg]->tmpGroupIDForRenaming = -1;
                        break;
                    }
                }
            }
            break;
        }
    default:
        return FALSE;
    }

    return TRUE;
}
